#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <Ticker.h>
#include <Arduino.h>
// 设置wifi接入信息（根据自己的手机热点进行修改）
const char* ssid = "Xiaomi";
const char* password = "12345678";
const char* mqttServer = "192.168.48.177";//这个是连接热点后电脑的ipv4地址
//电脑和esp-8266必须连接同一热点，才能保证esp-8266可以找到服务器
//此处服务器用的是本地服务器，具体在文档中会进行说明

#define Aout A0 //MQ-2 AO 接 Arduino Uno A0口
int temp = 0; //临时变量，存储A0读取的数据


WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
 
// ****************************************************
//         mqtt平台的初始设置，包括订阅的主题，密码等等一些信息
// ****************************************************
const char* mqttUserName = "admin1";         // 服务端连接用户名(需要需要保持一致)
const char* mqttPassword = "123456";          // 服务端连接密码(需要需要保持一致)
const char* clientId = "esp8266";          // 客户端id (需要需要保持一致)
const char* subTopic = "test011";        // 订阅主题(需要保持一致)
const char* pubTopic = "smoke";        // 发布主题(需要保持一致)
const char* willTopic = "iotuser/led_yz";       // 遗嘱主题名称（目前功能没有用到）
// ****************************************************
 
//遗嘱相关信息（目前还未用到，先进行保留）
const char* willMsg = "esp8266 offline";        // 遗嘱主题信息
const int willQos = 0;                          // 遗嘱QoS
const int willRetain = false;                   // 遗嘱保留
 
const int subQoS = 0;            // 客户端订阅主题时使用的QoS级别（截止2020-10-07，仅支持QoS = 1，不支持QoS = 2）
const bool cleanSession = false; // 清除会话（如QoS>0必须要设为false）
 
bool ledStatus = HIGH;
Ticker ticker;
int count=0;//计时器，可以计算发送信息时间
String control="0";

void connectMQTTserver(){

  
 
  /* 连接MQTT服务器代码 */
  if (mqttClient.connect(clientId, mqttUserName, 
                         mqttPassword, willTopic, 
                         willQos, willRetain, willMsg, cleanSession)) { 
    Serial.print("MQTT Server Connected. ClientId: ");
    Serial.println(clientId);
    Serial.print("MQTT Server: ");
    Serial.println(mqttServer);    
    
    subscribeTopic(); // 订阅指定主题
  } else {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(5000);
  }   
}
 
// 收到信息后的回调函数
// 即接受服务器控制信息的函数
void receiveCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println("");
  Serial.print("Message Length(Bytes) ");
  Serial.println(length);
 
  if ((char)payload[0] == '1') {     // 如果收到的信息以“1”为开始
    ledStatus = LOW;
    digitalWrite(BUILTIN_LED, ledStatus);  // 则点亮LED。
    delay(1000);
    ledStatus = HIGH;
    control = '1';
  } 
  else if((char)payload[0]=='2') {
    ledStatus = LOW;
    digitalWrite(BUILTIN_LED, ledStatus);  
    delay(1000);
    ledStatus = HIGH;
    control = '2';
  }
  else{
    ledStatus = LOW;
    digitalWrite(BUILTIN_LED, ledStatus);  
    delay(1000);
    ledStatus = HIGH;
    control = '3';
    }
 
  pubMQTTmsg();
}
 
// 订阅指定主题
void subscribeTopic(){
 
  
  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  if(mqttClient.subscribe(subTopic, subQoS)){
    Serial.print("Subscribed Topic: ");
    Serial.println(subTopic);
  } else {
    Serial.print("Subscribe Fail...");
  }  
}
 
// 发布信息，向服务器发送检测到的烟雾浓度的模拟信号
void pubMQTTmsg(){
  char* pubMessage;
  
  temp = analogRead(Aout); //读取A0的模拟数据
  Serial.print("Analog value: ");
  Serial.println(temp); //串口输出temp的数据
  char tempStr[6]; // 定义一个足够大的字符数组用于存储转换后的字符串
  itoa(temp, tempStr, 10); // 将整数转换为字符串，基数为10（十进制）
  pubMessage = tempStr;
  // 输出pubMessage的值
  Serial.print("pubMessage value: ");
  Serial.println(pubMessage);
 
  // 实现ESP8266向主题发布信息
  if(mqttClient.publish(pubTopic, pubMessage)){
    Serial.println("Publish Topic:");Serial.println(pubTopic);
    Serial.println("Publish message:");Serial.println(pubMessage);    
  } else {
    Serial.println("Message Publish Failed."); 
  }

  delay(5000);  //延时500毫秒
}
 
// ESP8266连接wifi
void connectWifi(){
 
  WiFi.begin(ssid, password);
 
  //等待WiFi连接,成功连接后输出成功信息
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");  
  Serial.println(""); 
}


void tickerCount() {
  // 每3秒增加一次计数
  count++;}

// 将获取到的参数以json数据的形式发送到mqtt服务器
String sendJson(String data, String control)
{
  const size_t capacity = JSON_OBJECT_SIZE(1)+10;
  DynamicJsonDocument doc(capacity);
  doc["switch"] = data;
  doc["control"] = control;
  // switch是控制声音模块开关的变量
  // control是控制语音的种类的变量
  String josn;
  serializeJson(doc, josn);
  return  josn;
}
String sendJsonControl(String data)
{
  const size_t capacity = JSON_OBJECT_SIZE(1)+10;
  DynamicJsonDocument doc(capacity);
  doc["control"] = data;
  String josn;
  serializeJson(doc, josn);
  return  josn;
}


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);          // 设置板上LED引脚为输出模式
  digitalWrite(LED_BUILTIN, ledStatus);  // 启动后关闭板上LED
  Serial.begin(9600);                    // 启动串口通讯
  
  //设置ESP8266工作模式为无线终端模式
  WiFi.mode(WIFI_STA);
  
  // 连接WiFi
  connectWifi();
  
  // 设置MQTT服务器和端口号
  mqttClient.setServer(mqttServer, 1883);
  mqttClient.setCallback(receiveCallback);
 
  // 连接MQTT服务器
  connectMQTTserver();

  ticker.attach(1, tickerCount);
  pinMode(Aout, INPUT);//定义A0为INPUT模式

}
 
void loop() {
  // 如果开发板未能成功连接服务器，则尝试连接服务器
  if (!mqttClient.connected()) {
    connectMQTTserver();
  }
  if(mqttClient.connected()){
    if (count >= 3){
      pubMQTTmsg();
      count=0;
      if(temp<=500)//以模拟信号是否小于500为标准判断音响是否开关
      {
        Serial.print(sendJson("1",control));
      }
      else{
        Serial.print(sendJson("0",control));
      }

  }
  }

   // 处理信息以及心跳
   mqttClient.loop();
  
   
}
 
